package com.korion.android.devicedemon.core

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.IBinder
import android.os.Message
import android.text.TextUtils
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.korion.android.devicedemon.ISmsDemonService
import com.korion.android.devicedemon.MainActivity
import com.korion.android.devicedemon.R
import com.korion.android.devicedemon.base.JsonUtils
import com.korion.android.devicedemon.base.LocalSettings
import com.korion.android.devicedemon.push.PushAPI

class SmsDemonService : Service(), Handler.Callback {

    companion object {
        const val TAG = "SmsDemonService"

        const val KEY_CMD = "cmd"
        const val CMD_START_FOREGROUND = 1
        const val CMD_STOP_SERVICE = 2

        private const val NOTIFICATION_CHANNEL = "DeviceDemon"
        private const val NOTIFICATION_ID = 1699721479

        private const val MSG_RECEIVE_SMS = 0
        private const val MSG_CHECK_PUSH_TOKEN = 1

        private const val CHECK_TOKEN_INTERVAL_MS = 1000L * 60
    }

    private var foreground = false

    private val smsFilter = IntentFilter().apply {
        this.addAction(SmsReceiver.SMS_RECEIVED_ACTION)
        this.priority = Integer.MAX_VALUE
    }

    private val smsReceiver = SmsReceiver()

    private val workThread = HandlerThread("SMSDemonService_WorkThread")

    private var workHandler: Handler? = null

    private val serviceImpl = object: ISmsDemonService.Stub() {
        override fun onReceiveSmsMessage(json: String?) {
            Log.d(TAG, "onReceiveSmsMessage: $json")
            workHandler?.run {
                sendMessage(obtainMessage(MSG_RECEIVE_SMS, json))
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "register receiver")
        registerReceiver(smsReceiver, smsFilter)
        workThread.start()
        workHandler = Handler(workThread.looper, this)
    }

    override fun onBind(intent: Intent): IBinder {
        return serviceImpl
    }

    override fun handleMessage(msg: Message): Boolean {
        when (msg.what) {
            MSG_RECEIVE_SMS -> {
                val json = msg.obj ?.let { it as String } ?: return true
                val smsMessages = JsonUtils.fromJson(json, SmsMessage::class.java) ?: return true

                val remote = LocalSettings.getString(LocalSettings.KEY_REMOTE_PHONE)
                if (remote.isNullOrEmpty() && TextUtils.equals(remote, smsMessages.from)) {
                    doRemoteCommand(smsMessages.content)
                    return true
                }
                // 先通过Push转发消息，如果不成功则使用短信转发
                pushMessage(smsMessages.toString()) { success, detail->
                    if (!success) {
                        Log.d(TAG, "push message failed: $detail")
                        if (!remote.isNullOrEmpty()) {
                            SmsUtils.sendSms(this, remote, smsMessages.toString())
                        }
                    } else {
                        Log.d(TAG, "push message success")
                    }
                }
            }
            MSG_CHECK_PUSH_TOKEN -> {
                checkPushAccessToken()
                workHandler?.run {
                    sendMessageDelayed(obtainMessage(MSG_CHECK_PUSH_TOKEN), CHECK_TOKEN_INTERVAL_MS)
                }
            }
        }
       return true
    }


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val cmd = intent?.getIntExtra(KEY_CMD, 0)
        Log.d(TAG, "onStartCommand: cmd=$cmd")
        when (cmd) {
            CMD_START_FOREGROUND -> {
                val notification = buildNotification()
                startForeground(NOTIFICATION_ID, notification)
                foreground = true
                workHandler?.run {
                    sendMessageDelayed(obtainMessage(MSG_CHECK_PUSH_TOKEN), 3000)
                }
            }
            CMD_STOP_SERVICE -> {
                stopSelf()
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }


    private fun buildNotification(): Notification {
        val pendingIntent = Intent(this, MainActivity::class.java)

        val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channelId = createNotificationChannel(NOTIFICATION_CHANNEL)
            NotificationCompat.Builder(this, channelId)
        } else {
            NotificationCompat.Builder(this)
        }
        builder.setContentIntent(PendingIntent.getActivity(this, 0, pendingIntent,
            PendingIntent.FLAG_IMMUTABLE))
            .setLargeIcon(BitmapFactory.decodeResource(this.resources, R.mipmap.ic_launcher))
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle(this.resources.getString(R.string.app_name))
            .setContentText("SMS listening")
            .setWhen(System.currentTimeMillis())
            .setPriority(NotificationManager.IMPORTANCE_HIGH)
            .setOngoing(true)
        return builder.build()
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String): String {
        val channel = NotificationChannel(channelId, "device demon sms listener", NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.WHITE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        manager.createNotificationChannel(channel)
        return channelId
    }


    private fun checkPushAccessToken(){
        val token = LocalSettings.getString(LocalSettings.KEY_PUSH_ACCESS_TOKEN)
        val tokenExpired = LocalSettings.getString(LocalSettings.KEY_ACCESS_TOKEN_EXPIRED)?.toLong() ?: 0L
        if (token.isNullOrEmpty() || tokenExpired <= System.currentTimeMillis() - CHECK_TOKEN_INTERVAL_MS * 10) {
            Log.d(TAG, "checkPushAccessToken: flushing token")
            val appId = LocalSettings.getString(LocalSettings.KEY_PUSH_APP_ID)
            val appSecret = LocalSettings.getString(LocalSettings.KEY_PUSH_APP_SECRET)
            if (appId.isNullOrEmpty() || appSecret.isNullOrEmpty()) {
                Log.e(TAG, "appId or appSecret is null")
                return
            }
            val tokenModel = PushAPI.getImpl().getAccessToken(appId, appSecret)
            if (tokenModel?.code == 0) {
                Log.e(TAG, "get access token success: ${tokenModel.toString()}")
                LocalSettings.saveString(LocalSettings.KEY_PUSH_ACCESS_TOKEN, tokenModel.accessToken)
                LocalSettings.saveString(LocalSettings.KEY_ACCESS_TOKEN_EXPIRED, (System.currentTimeMillis() + tokenModel.expire * 1000).toString())
            } else {
                Log.e(TAG, "get access token failed: ${tokenModel?.code}, ${tokenModel?.message}")
            }
        } else {
            Log.d(TAG, "checkPushAccessToken: token is valid")
        }
    }

    private fun pushMessage(message: String, callback: ((Boolean, String)->Unit)?) {
        val token = LocalSettings.getString(LocalSettings.KEY_PUSH_ACCESS_TOKEN)
        val tokenExpired = LocalSettings.getString(LocalSettings.KEY_ACCESS_TOKEN_EXPIRED)?.toLong() ?: 0L
        if (token.isNullOrEmpty() || tokenExpired <= System.currentTimeMillis()) {
            callback?.invoke(false, if (token.isNullOrEmpty()) "token is empty" else "token has been expired")
            return
        }
        val openId = LocalSettings.getString(LocalSettings.KEY_PUSH_OPEN_ID) ?: ""
        if (openId.isEmpty()) {
            callback?.invoke(false, "openId is empty")
            return
        }
        val result = PushAPI.getImpl().pushTextMessage(token, openId, message)
        if (result?.code == 0) {
            callback?.invoke(true, "success")
        } else {
            callback?.invoke(false, "code=${result?.code}, ${result?.message}")
        }
    }

    private fun doRemoteCommand(cmd: String){

    }

    override fun onDestroy() {
        unregisterReceiver(smsReceiver)
        if (foreground) {
            stopForeground(true)
        }
        workHandler?.removeCallbacksAndMessages(null)
        workHandler = null
        workThread.quitSafely()
        super.onDestroy()
    }

}
